{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Quantum Approximate Optimization Algorithm \n",
    "\n",
    "Qiskit has an implementation of the Quantum Approximate Optimization Algorithm [QAOA](https://qiskit.org/documentation/stubs/qiskit.algorithms.minimum_eigensolvers.QAOA.html) and this notebook demonstrates using it for a graph partition problem.\n",
    "\n",
    "Before we begin, let's import the `annotations` module from `__future__` to allow postponed evaluation of annotations. This enables us to use simpler type hints throughout the notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from __future__ import annotations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First we create a graph and draw it so it can be seen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import networkx as nx\n",
    "\n",
    "num_nodes = 4\n",
    "w = np.array([[0., 1., 1., 0.],\n",
    "              [1., 0., 1., 1.],\n",
    "              [1., 1., 0., 1.],\n",
    "              [0., 1., 1., 0.]])\n",
    "G = nx.from_numpy_array(w)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "scrolled": true,
    "tags": [
     "nbsphinx-thumbnail"
    ]
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAb4AAAEuCAYAAADx63eqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAAsTAAALEwEAmpwYAAA2uUlEQVR4nO3dZ0BUZ94F8DMNBESsJEqJgtggtmhiAWsQRcUodoEoGBNNYqKuxvgmZvc1TbH3hhVL1AQrKqAGwTUYo2KNmljBhuKidJiZ9wOLrwULMDPPnbnn98UszNw5q+i5/9sehV6v14OIiEgmlKIDEBERmRKLj4iIZIXFR0REssLiIyIiWWHxERGRrLD4iIhIVlh8REQkKyw+IiKSFRYfERHJCouPiIhkhcVHRESywuIjIiJZYfEREZGssPiIiEhWWHxERCQrLD4iIpIVFh8REckKi4+IiGSFxUdERLLC4iMiIllh8RERkayoRQd4Vbm5KcjKSkZhYQaUygqwtnaGvX1LKBQK0dGIiMiMSLr49Hod7t/fh+vXpyEjIxEKhTUAHQAFAB3U6ipwcfkHXn/9fajVDoLTEhGROVDo9Xq96BAlyc+/g+RkX+TmXoJWm/nc1ymVtgAU8PTcjGrVupkuIBERmSVJFl9+/m0cPfoW8vPvACh4pfcolTZo0GAVHB37GzccERGZNckVn05XgKNHmyI7+wKAwlK9V6m0RdOm+1Gp0jvGCUdEZkWv1+P3G78j+mI0bmXeAgDUsq+FgPoBaPp6U7HhSBjJFd+dO5tx/nzoCw9vvoiDQ3s0a/arYUMRkVnJK8zDulPrMPXQVKQ+SEVOQQ500AEAVAoVrNXWcK/iji/afoH+nv2hUWkEJyZTklzxHT3aApmZf5T5/UplBbRseRY2NnUMmIqIzMW97Ht4d+27uHjvIrIKsl74WjuNHZq+3hTRQ6JRybqSiRKSaJK6jy8r6yyys8+Waxt6vQ4pKXMNlIik6Pvvv4ezszPc3d2RkZFR4mvmzp2Lli1bokWLFjh16pSJE5IoGbkZeGf5Ozhz58xLSw8AsgqycPTGUfis9EFOQY4JEpIUSKr4MjIOoehWhbLT6/Nx/36cYQKRJPn6+mLv3r2wtbWFVqt95vvHjx/H9u3bsWrVKnz//feYMGECMjPLduiczEv/Lf2R8iAFBbpXuygOAPK0ebhw7wKGbRtmxGQkJZK6j6+w8D/Q61/9B/Z50tIu49NPP4VarYZKpXri15K+VprXGPr9vAG/9Fq2bIn09HQolUqoVKpnvr9x40b06tULnp6e8PT0xOjRo3H79m1UrFhRQFoylfN3zyPhagLytHmlfm9uYS62nd+GGw9voJZ9LSOkIymRVPEplVYwxBCq0djAw8MDWq0WhYWFT/yan5//zNce//VF33uV15Tm/Vqt9tE/3lIuZ1Gf8aIdA51OB71eX2Lx/f333+jcuTN0Oh2USiVcXFxw69YtuLu7l/tni6RrTtIcFOpKdyX44/R6PRYfXYz/7fi/BkxFUiSp4rOyqgml0graMuyxPa569foYPXq0gVIZj16vh06nM2q5lvZ7+fn5yM7ONukOwPO+p9Pp4OLignPnzsHOzu6J3zud7r9X6JVQfIWFhVCr1VAqi3airKysSjwkev36dUyYMMFsdyB4xOD/5RXmYXXy6lId4nxmG9o8zD8yH//s8E8oFZI6C0QGJqniq1rVH3p92ffYAEClqohatT4yUCLjUigUj/4Bo2fp9XpotdoSf3+KJ77icnuco6Mj0tPTHxVgSkoKatas+czr7O3tERAQUKpyzsvLe2bHQNROik6ne/TzI3o6F30E4Wb2TcAA16dnFWThYd5DOFTgIxAtmaSKT62uiNdeG4KbN1ehtDevP65Gjb4Gy0TiKBQKqNUl/4gqFAoolUpYW1s/8z1/f3+sWrUKffv2RWpqKvLz8+Hq6vrM6ypXroxBgwYZPLepFO8YSGE6L/7v3NxcIacQ8hzykN0/G3j2x6FU1Eo1HuQ9YPFZOEkVHwA4O4/B7dvroCvDsXqFwho1a34AlaqCEZKRVAwdOhSxsbG4desWXFxc8K9//QtarRZ6vR4jRoxAz549ERUVhcaNG0OhUGDatGklFqS5K94xeN7OgZxcun8JjRc1fqVbGF6kUFcIOyu7l7+QzJrkbmAHgCtXpuDatR+h02W/8nu0WiAz0wFdu16FtTX31ixd8bRTfCi0+HxpccFlZmYiPT0dCoUCzs7OPBdm4bILslF1atUyXdH5ODuNHTImZkCl5OkHSybJM7hvvPEVatUa+d+VF15OoagAW9u6WLWqCQYNGobc3FwjJyTRiqcda2trqNVqaDSaJ6a6ihUrwtXVFS4uLiw9GbDV2KJPwz7luihFo9RgWLNhLD0ZkGTxKRQK1K07HfXqLYSVVS2oVCXff6VU2kKprABHx0Fo2fIYNm6MgVqthr+/Px48eGDi1EQk0rjW41BBXfbTHCqlCp+985kBE5FUSfJQ5+OKFqPdj+vXpyEz8zi02iwoFFawsnoNTk6f4PXXQ/D4IrRarRYff/wxjh49it27d6NGjRoC0xORKTVf0hyn7pwq9f18GqUG3q7e2P/+fiMlIymRfPGVhV6vx+TJk7Fp0ybExsaWeEUfEVmeGw9voMniJkjPSYdOr3ul96gUKtSsWBMnPjqBarbVjJyQpECShzrLS6FQYMqUKRg5ciS8vb1x9mz5HnxNROahln0t/Bb2G2pWrAkrpdVLX2+tssYbld/A4eGHWXoyYpHFV+zzzz/Hd999h06dOiEpKUl0HCIyAfeq7kj+KBlNcprASmcFeyv7Z16j1qphq7DFRO+JOP7hcThXchaQlESxyEOdT9u5cydCQ0Oxbt06+Pr6io5DREZ269YtNGrUCMeSj+HIgyPYfGYz7mTdgUKhwOsVX4d7rjv2L9qPw4cOi45KAsii+AAgISEBffv2xfz589GvXz/RcYjIiMaNG4fCwkLMmTOnxO8XFBTA2dkZiYmJ8PDwMHE6Ek02xQcAycnJ8Pf3x9dff42PPjKP53kSUekUT3unT59GrVrPX2Lo888/h4ODA/71r3+ZMB1JgayKDyhassbX1xdhYWGYNGkSb24msjDjxo1DQUEB5s6d+8LXHTt2DIGBgfj7779LfNg5WS7ZFR8A3Lx5E35+fujcuTNmzJjBH3oiC3H79m00bNgQp06dgpOT0wtfq9fr4eXlhcWLF8PHx8dECUkKZPkvfs2aNREfH48jR45g6NChKCgo/6rvRCReeHg4goKCXlp6QNFtTyEhIVi7dq0JkpGUyHLiK5adnY2+fftCpVJh06ZNsLGxER2JiMqoNNNesZSUFDRu3Bipqan8+y8jspz4itna2mLbtm1wcHCAn58f/vOf/4iORERlFB4ejiFDhrxy6QGAs7Mzmjdvjh07dhgxGUmNrIsPADQaDdasWYOmTZuiQ4cOuHXrluhIRFRKt2/fxooVKzBx4sRSvzckJARr1qwxQiqSKlkf6nycXq/Ht99+i9WrVyMmJgZubm6iIxHRKxo/fjxyc3Mxb968Ur83MzMTzs7OuHDhAhwdHY2QjqSGxfeURYsW4bvvvkN0dDQaN24sOg4RvcSdO3fQoEGDUp3be1pwcDBatGiBzz7jskRyIPtDnU8bOXIkZsyYAV9fXxw6dEh0HCJ6ibKc23saD3fKCye+59i7dy+CgoKwevVq+Pv7i45DRCUonvZOnjwJZ+eyP2haq9XC1dUVMTEx8PT0NGBCkiJOfM/h5+eHHTt2YNiwYVi3bp3oOERUgvDwcAwePLhcpQcAKpUKQUFBvKdPJjjxvcSZM2fQtWtXjB8/HqNHjxYdh4j+y1DTXrHTp0+ja9euuHr1KlQqlQESklRx4nsJT09PJCQkYP78+fjmm2/A/QQiaZg+fbpBpr1iXl5ecHR0xK+//mqQ7ZF0ceJ7Rbdv30a3bt3QunVrzJs3j8/3JBLI0NNesdmzZ+P48eNYvXq1wbZJ0sPiK4WMjAz06tULNWvWxOrVq2FlZSU6EpEsTZgwAVlZWViwYIFBt3v79m3Ur18fKSkpqFixokG3TdLBsaUUHBwcsGfPHuTk5CAgIABZWVmiIxHJzp07d7B8+fIyPaXlZV577TW0bdsWUVFRBt82SQeLr5QqVKiALVu2oGbNmnj33XeRnp4uOhKRrEyfPh2DBg2Ci4uLUbbPFRssHw91lpFer8eECROwe/du7N27t1w3zxLRq0lLS0ODBg1w4sQJoxVfTk4OnJycyvUkGJI2TnxlpFAoEB4ejpCQEHh7e+PixYuiIxFZvOnTp2PgwIFGKz0AsLGxQZ8+fXj/rgXjxGcAy5cvx+TJk7Fr1y40a9ZMdBwii5SWlob69esjOTnZqMUHAAcPHsSoUaNw6tQpKBQKo34WmR4nPgMYPnw45s+fDz8/P94DRGQkppj2inl7eyMrKwsnTpww+meR6XHiM6D9+/dj4MCBWLZsGXr16iU6DpHFMOW0V+zrr79GZmYmZs2aZZLPI9Nh8RnY0aNH0bNnT3z//fcYNmyY6DhEFmHixIl48OABFi5caLLPvHDhAnx8fJCamgq1Wm2yzyXj45+mgbVo0QIHDhyAn58f0tPTMW7cONGRiMxaWloali1bZvLDjvXq1YObmxtiYmK4QouF4Tk+I2jQoAESExOxfPlyfPnll3y+J1E5zJgxA/379zfZIc7HBQcHc50+C8RDnUZ09+5d+Pv7o0mTJli8eDGf+E5USsXn9k6cOAFXV1eTf/69e/fg5uaGa9euwcHBweSfT8bBic+Iqlevjn379uHKlSvo378/cnNzRUciMiszZszAgAEDhJQeAFSrVg2dO3fGli1bhHw+GQcnPhPIy8tDUFAQ0tPTsXXrVtjb24uORCR5d+/eRf369XH8+HFhxQcAW7duxaxZsxAfHy8sAxkWJz4TsLa2xsaNG+Hh4YFOnTohLS1NdCQiySs+tyey9ADA398fZ86cwZUrV4TmIMNh8ZmISqXCokWL4OfnBx8fH1y7dk10JCLJunv3LpYuXYovv/xSdBRYWVlhwIABiIyMFB2FDITFZ0IKhQLffvstPvzwQ3h7e+PcuXOiIxFJ0owZM9CvXz/h016xkJAQrFmzhldoWwjexyfAmDFjUK1aNXTs2BHbt2/H22+/LToSkWTcvXsXS5YswfHjx0VHeaT472hSUhJatWolOA2VFyc+QUJCQrBs2TL06NEDcXFxouMQScbMmTPRv39/vPHGG6KjPKJQKB5NfWT+eFWnYAcPHkTfvn2xcOFC9O3bV3QcIqGKr+Q8duyYpIoPAK5cuYIWLVogNTUV1tbWouNQOXDiE6xdu3aIiYnB6NGjsXTpUtFxiISaOXMm+vbtK7nSA4DatWvDy8sL0dHRoqNQOXHik4i//voLXbp0wQcffICJEydyDTCSHSlPe8UiIiKwc+dOREVFiY5C5cDik5AbN27Az88Pvr6+mD59OpRKDuQkH5MmTcK9e/ewZMkS0VGeKyMjA66urrh06RKqVasmOg6VEYtPYu7fv48ePXqgbt26WL58OTQajehIREZ379491KtXT9LTXrFBgwbBx8cHo0aNEh2FyogjhcRUqVIFMTExSEtLQ2BgIHJyckRHIjI6KZ/bexpXbDB/nPgkqqCgAEOHDsX169exY8cOPhmeLFbxtPfHH3+gdu3aouO8VGFhIZydnREfH4/69euLjkNlwIlPojQaDdauXYsmTZqgffv2uHXrluhIREYxc+ZMBAYGmkXpAYBarcbgwYP5CDMzxolP4vR6PaZMmYI1a9YgNjYWderUER2JyGDMbdordvz4cfTu3RuXLl3iRWhmiH9iEqdQKDB58mSMHTsWPj4+OHXqlOhIRAYza9Yss5r2ijVt2hT29vZISEgQHYXKgBOfGdmwYQM+//xzREVFoU2bNqLjEJWLuU57xcLDw3H+/HksX75cdBQqJRafmdmzZ8+jq8q6desmOg5RmX311Ve4ffs2li1bJjpKmdy4cQNeXl5ITU2FjY2N6DhUCiw+M3T48GG89957mD17NgYNGiQ6DlGpFU97R48eNevz1l26dEFoaCgGDhwoOgqVAs/xmaHWrVtj3759GD9+PObPny86DlGpzZo1C3369DHr0gPAFRvMFCc+M3b58mV06dIFQ4YMwTfffMPne5JZSE9Ph4eHh9lPewCQlZUFJycn/Pnnn3j99ddFx6FXxInPjNWpUweJiYnYtm0bPv30U+h0OtGRiF7KUqY9ALCzs0OvXr2wYcMG0VGoFDjxWYCMjAwEBATAyckJq1atgpWVlehIRCWypGmv2L59+/CPf/xDUivG04tx4rMADg4O2LNnD7KystCrVy9kZWWJjkRUolmzZqF3794WU3oA0KFDB9y9e5f32JoRTnwWpLCwEMOHD8eFCxewc+dOVK1aVXQkokeKp73ff/8dbm5uouMY1JdffgmtVotp06aJjkKvgBOfBVGr1VixYgXatGmDdu3aITU1VXQkokdmz56N3r17W1zpAUUrNqxbtw5arVZ0FHoFatEByLCUSiXCw8NRo0YNeHt7IyYmBh4eHqJjkcylp6dj4cKFOHLkiOgoRtGoUSPUrFkT+/fvh6+vr+g49BKc+CyQQqHAF198gUmTJqF9+/Y86U7CzZ49G++9955FTnvFeE+f+eA5Pgv3888/Y+TIkdiyZQvatWsnOg7JkCWf23vcnTt3UK9ePVy/fh329vai49ALcOKzcIGBgdiwYQP69u2L7du3i45DMiSHaQ8AHB0d4ePjg19++UV0FHoJTnwy8fvvv6Nnz56YOnUq3n//fdFxSCbu378PDw8PHDlyxOKLDwA2b96MJUuWIC4uTnQUegEWn4z8+eef8PPzw2effYaxY8eKjkMy8M033yAlJQURERGio5hEbm4uatWqheTkZLi4uIiOQ8/B4pOZ69evo0uXLujduze+++47Pt+TjEZu016xESNGwM3NDRMnThQdhZ6D5/hkxsXFBQkJCYiLi8OHH37I+47IaGbPno2AgABZlR5QdHXn2rVrwZlCujjxydTDhw/Ru3dvVK5cGevWrYO1tbXoSGRBiqe9pKQkuLu7i45jUnq9Hu7u7ti8eTPeeust0XGoBJz4ZMre3h67du0CAHTv3h15eXnPfa1Op8P58+dNFY0sQGpqKoYPHy670gOK7qMNDg7mPX0SxolP5rRaLbZs2YKAgADY2NiU+Jrdu3dj/PjxGDduHIYNG2bihGSOCgoKoFKpoFTKc9/6r7/+Qps2bZCamgqNRiM6Dj1Fnj+V9IhKpUL//v2fW3oA0K1bN/Tt2xeTJ0/G3bt3TZiOzJVGo5Ft6QFA3bp14eHhgb1794qOQiWQ708mPfKyKzvj4uKwc+dOzJs3D9WrVzdRKiLzxsOd0sVDnfRCv//+Oz788EN8+umnPMxJz6XT6WQ94ZUkPT0dderUwdWrV1G5cmXRcegx/EmlZ9y4cQMAcO7cOXz66acIDg5+VHrcT6LHPXz4EABYeiWoWrUqfH19sXnzZtFR6Cmc+OgJOTk56N69O1q0aIHTp0+jVatWmDx5MoD/L73HD43evXuXhz9lavLkybh48SKuXr2K0NBQtG/fHnXr1oVCoYBWq4VKpRIdUbjt27cjPDwcCQkJoqPQY1h89Ixr167Bz88Pubm5uHz5MoCi1d1VKtWj0tu+fTuOHz+Ow4cPw9bWFuvXr0eFChVExiYT2rx5M3788UfExMQgLi4OP//8M/Lz89GrVy8eEn9Mfn4+nJyckJSUJLsb+aWMxyfoGa6urvj3v/8NFxcX7NmzB0DR6u7Fpbd06VKsX78e1atXx6JFi+Ds7IxRo0aJjEwmdvr0aXTt2hXVqlXDgAEDsGnTJowYMQKLFi1CUFAQCgoKREeUBCsrKwwcOBCRkZGio9BjWHxUoipVquDXX3+FQqF4YpmVqKgo7Ny5E6GhoRgyZAjq1KmDgQMHwsHBQWBaMrWBAwfi3Llz2L17NzIyMgAA/v7+OHLkCJRKJa5cuSI2oIQUL1DLg2vSweKj51IqlfDz84Ner0dqaioyMjKQmJiIIUOGoE2bNqhcuTJyc3Mxbtw41KxZU3RcMqGGDRuiZ8+eWL16NVauXInk5GRcunQJeXl5iI2N5U3bj2nRogXUajUOHz4sOgr9F8/x0StLS0tDcHAwpk+fDi8vLwBFT6K3srLC/PnzARRdAMMVH+QjISEBq1evRk5ODpRKJVJSUvDOO+/gxx9/FB1NUr7//ntcu3YNixcvFh2FwOKjUjh+/Dg+/vhj7Nu3DzY2Nhg0aBBsbW0xadIkWT6TUY6Kd2ye3sE5ffo0lEolKlasCEdHR17o9JRr166hWbNmuHHjBh8ILwFq0QHIfDRr1gxdunRB8+bN0ahRIwDA4sWLeX5PRhQKBXQ63RP/rVAo4OXlhcLCQqjVap7LKoGrqysaN26MnTt3IjAwUHQc2ePER6V25swZ2Nvbw9XVVXQUMqGVK1eia9euj87n6nQ66PV6qFQqFBQUYOXKlQgODn7hc1/lbOXKldi6dSu2bdsmOorssfioXIofVcVHVlm2LVu2YNCgQfD09ESHDh0wYcIE1KpV69H3jxw5glu3biEgIEBgSml78OABXF1d8ddff/GhD4LxXyoqF6VSCa1Wi8uXL+PIkSOi45CR7N69G7NmzcL27duRl5cHPz8/jB49Gvfu3QNQ9CDzjh07Ck4pbZUqVUL37t2xceNG0VFkj8VH5aZSqXDlyhV079790Q3vZFnmz5+PgIAAuLq6YtGiRdiwYQMKCgrg7++POnXq4I8//oC9vb3omJLHFRukgYc6yWD+/e9/o3fv3pg9ezYGDRokOg6ZwB9//AFvb2+cPXsWderUER1H8goLC+Hi4oIDBw6gQYMGouPIFic+Mpg2bdogLi4O48ePx4IFC0THIRM4fPgwBgwYwNJ7RWq1GkOGDMHatWtFR5E1TnxkcJcvX0aXLl0QFBSEyZMn84Z2C5KXlwe9Xv/oPr2cnBwUFBSgUqVKgpOZj+TkZPTs2RNXrlzhBWGC8HedDK5OnTpITExEVFQURo8e/ei+LzJvGRkZqFev3qM1+ADAxsaGpVdKTZo0QZUqVRAfHy86imyx+MgoXnvtNcTHx+PkyZMICgpCfn6+6EhUTvPmzUP79u1Ro0YN0VHMXkhICA93CsRDnWRUOTk5GDBgAAoKCrBlyxbY2dmJjkRlkJGRgbp16+LQoUOoV6+e6Dhm7+bNm2jUqBFSU1Nha2srOo7scOIjo7KxscEvv/wCR0dH+Pr6Ij09XXQkKoN58+aha9euLD0DqVmzJt555x1s3bpVdBRZYvGR0anVaqxcuRKtWrVC+/btcePGDdGRqBQePHiAOXPm4KuvvhIdxaLwcKc4LD4yCaVSiRkzZmDw4MHw9vbGX3/9JToSvaLiaa9+/fqio1iU9957D7/99htu3rwpOors8BwfmdzSpUvxz3/+E9HR0WjatKnoOPQCDx48gLu7OxITE1l8RjBs2DB4eXlh3LhxoqPICic+MrkRI0Zg7ty56NKlCw4ePCg6Dr3AvHnz4Ofnx9IzkpCQED7CTABOfCRMXFwcBg8ejIiICPTs2VN0HHpK8bSXkJDAx2sZiU6nQ+3atbFjxw40adJEdBzZ4MRHwrz77rvYuXMnPvjgA+71StD8+fPh5+fH0jMipVKJoKAgXuRiYpz4SLhz587Bz88PY8aMwZgxY0THIRRNe3Xr1sXBgwdZfEZ27tw5dOrUCdevX4darRYdRxY48ZFwDRs2RGJiIpYsWYL/+Z//AffFxJs/fz66dOnC0jOBhg0b4o033sCJEydER5ENTnwkGWlpafD390fz5s2xcOFCqFQq0ZFkidOe6eXm5kKv18PGxkZ0FFngxEeSUaNGDezfvx9//fUXBg4ciLy8PNGRZGn+/Pnw9fVl6ZlQhQoVWHomxImPJCcvLw+DBw9GRkYGoqKiuLK3CT18+BDu7u6c9siiceIjybG2tsamTZtQp04ddO7cGXfv3hUdSTY47UmLVqvFvn37MGXKFNFRLAonPpIsvV6PSZMmYevWrYiJiYGLi4voSBaN0540nTp1Cu+++y7+/vtvVKxYUXQci8CJjyRLoVDghx9+wPDhw+Ht7Y0///xTdCSLxmlPrOTk5Cf+9927d7F+/XrMnDkTBQUFOH36tKBklocTH5mFVatW4csvv8SOHTvQokUL0XEsTvG0Fx8fj4YNG4qOI0vdunVDcHAwGjZsiKVLlyIpKQlVqlRB7969MXjwYFStWlV0RIvBuyXJLAwdOhRVqlSBv78/Nm7ciE6dOomOZFEWLFiAd999l6UnUL9+/RAUFIR27dqhTZs2WL9+/RPTt06ng1LJg3SGwImPzEp8fDz69euHxYsXo0+fPqLjWAROe9KQl5eHqlWrIj09HdbW1o++rtVqeU+rgXH3gcxK+/btsXfvXnzyySdYvny56DgWgdOeNFhbW2PatGk4d+4cgKLCA4qe51k8n3BOMQxOfGSWLl68iC5duuCjjz7ChAkToFAoREcyS5z2pCUlJQUJCQkYNGgQJz0j4jk+MkseHh5ITEyEn58f0tLSEB4ezvIrgwULFqBz584sPYlwdnZGeno6MjMzH926cObMGcTFxUGtVsPV1RV169bln1c5ceIjs5aeno7u3bujQYMGWLZsGZ9uXwqZmZlwd3fHgQMH0KhRI9Fx6L+ysrJgZ2eH48ePY8mSJUhNTYWnpyeqVq2KEydOIDU1FfHx8aJjmjUWH5m9rKwsBAYGwtraGhs3buQzD1/R1KlTceLECWzYsEF0FHrK/fv38fXXX8Pd3R2+vr6oVasWNBoN7O3t4e3tjTFjxiAwMFB0TLPFi1vI7NnZ2WH79u2wsbFBt27dkJGRITqS5GVmZmLmzJn4+uuvRUehEixatAi5ubkIDQ2Fl5cXqlatCnt7e9y4cQP16tWDh4eH6IhmjcVHFsHKygrr1q2Dp6cnOnbsiDt37oiOJGkLFixAp06deIhTojQaDfLy8uDg4AAAiI2NxciRI9GhQwdUq1YNXl5eghOaNx7qJIui1+vxz3/+Exs2bEBMTAxq164tOpLk8Nye9KWlpSEsLAwajQZnz55FvXr10KZNGwQGBqJu3bqi45k9Fh9ZpHnz5mHatGnYs2cPPD09RceRlGnTpuHYsWPYuHGj6Cj0Avn5+YiPj4ebmxscHR2h0WhQoUKFR/fy8SrmsmPxkcVat24dxo4di61bt6J169ai40hC8bS3f/9+7hCQbPEcH1msIUOGYOXKlQgICMDevXtFx5GEhQsXomPHjiw9kjVOfGTxDh06hD59+mDOnDkYOHCg6DjCcNozX/n5+dBoNDy8aSC825csXtu2bREXF4du3bohPT0do0aNEh1JiIULF6JDhw4sPTMUFRUFBwcHdO3aVXQUi8DiI1l48803cfDgQXTp0gV3797F119/Lau956ysLMycORNxcXGio1AZ5ObmIjIyksVnIDzUSbJy69YtdO3aFe3atcPs2bNls75ZeHg4jh49ip9++kl0FCqDhw8fwsXFBRcuXICjo6PoOGaPxUey85///Ac9e/aEq6srVq1aBY1GIzqSUWVlZcHd3R1xcXG88dmMBQcHo2XLlhg9erToKGZPHru7RI+pXLky9u7diwcPHuC9995Ddna26EhGtXDhQrRv356lZ+aCg4OxZs0a0TEsAic+kq2CggKEhYXh0qVL2LFjB6pUqSI6ksFx2rMcWq0Wrq6uiI2N5RN3yokTH8mWRqPBqlWr0LJlS7Rv3x43b94UHcngFi1ahHbt2rH0LIBKpcKQIUOwdu1a0VHMHic+kj29Xo8ffvgBERERiImJgbu7u+hIBsFpz/KcOnUK/v7+uHLlCldnLwdOfCR7CoUCkyZNwoQJE9CuXTskJyeLjmQQnPYsz5tvvonq1avj119/FR3FrHHiI3rM5s2b8fHHH+Pnn3+Gj4+P6DhlxmnPcs2aNQvJyclYtWqV6Chmi8VH9JTY2FgMGTIEK1asQI8ePUTHKZPp06cjKSkJmzdvFh2FDOzWrVto0KABUlNTYWdnJzqOWeKhTqKn+Pr6YufOnRg+fLhZXj6elZWF6dOnY/LkyaKjkBG8/vrraNOmDaKiokRHMVssPqISvP322zhw4AC++uorzJ49W3ScUlm8eDF8fHzw5ptvio5CRhISEsKrO8uBhzqJXuDatWvw9fVFv379MGXKFMk/37P43F5sbCyLz4Ll5OTAyckJp06dgpOTk+g4ZocTH9ELuLq6IjExEXv27MHIkSOh1WpFR3ohTnvyYGNjg969e2P9+vWio5glTnxEr6D48WbVq1fH2rVrYW1tLTrSM7Kzs+Hm5sZpTybi4+PxySef4OTJk5I/EiE1nPiIXkGlSpUQHR2NwsJC9OzZE5mZmaIjPWPx4sXw9vZm6cmEj48PHj58aDH3nZoSi4/oFVWoUAGbNm2Cq6srOnfujHv37omO9Eh2djbCw8N5JaeMKJVKBAUFmeWVx6Kx+IhKQa1WY9myZejYsSN8fHyQkpIiOhKAommvbdu2aNy4segoZELBwcFYv349CgsLRUcxKyw+olJSKBT48ccfERoaCm9vb5w/f15oHk578lW/fn3Url0bsbGxoqOYFRYfURn94x//wDfffIMOHTrgjz/+EJZj8eLFaNOmDac9mQoJCeHhzlLiVZ1E5bR161aMGDECP/30Ezp27GjSz87Ozoa7uzv27NmDJk2amPSzSRru3bsHNzc3XLt2DQ4ODqLjmAVOfETl9N5772HTpk0YMGAAfvnlF5N+9pIlS9CmTRuWnoxVq1YNnTp1ws8//yw6itngxEdkIMeOHUOPHj0wZcoUhIWFGf3zOO1RsaioKMyZM4fLFb0iFh+RAV24cAFdunTBqFGjMGHCBKN+1qxZs5CYmMg9fUJeXh6cnJxw9OhR1K5dW3QcyWPxERlYSkoK/Pz80L17d0ydOtUoT9XgtEdPGzVqFGrVqoWvvvpKdBTJ4zk+IgNzdnbGwYMHcfDgQQwfPtwo91gtXboUrVu3ZunRI8UrNnCWeTlOfERGkpmZicDAQNja2mLDhg2oUKGCQbabk5MDd3d3REdHo2nTpgbZJpk/vV6P+vXrY+3atXjnnXdEx5E0TnxERlKxYkXs2LED1tbW6NatGx48eGCQ7S5ZsgStWrVi6dETFAoFgoODeU/fK+DER2RkWq0Wn376KZKSkrB79244OjqWeVuc9uhFLl++jLfffhupqamwsrISHUeyOPERGZlKpcKCBQvQo0cPeHt74+rVq2XeFqc9epE6deqgUaNGiI6OFh1F0jjxEZnQ3LlzER4ejj179sDT07NU7+W0R69i+fLliI6ONvnDFMwJi4/IxNatW4exY8di27ZtaNWq1aOv63TAvn3AtGnAb78B2dmASgVUqQK8/z5ga7sCyck7EBUVJTA9SV1GRgZcXV1x6dIlVKtWTXQcSWLxEQkQHR2N999/H5GRkfDz88NPPwFjxgAPHwIlrXFrba1HXl4eWrYswM8/28PFxfSZyXwMHDgQ7du3x8iRI0VHkSQWH5Eghw4dQp8+fdCxYyx27GiM7OyXv0elAhwcgPh4wMvL+BnJPO3atQvffvstDh8+LDqKJLH4iAT66qsUfPddVQC2pXpf1apAcjLg7GycXGTeCgoK4OzsjISEBNSrV090HMnhVZ1Egty+DcyY4YzSlh4AZGQAI0YYPhNZBo1Gg8GDByMyMlJ0FEli8REJsnRp2d+r1QIHDgA3bhguD1mW4keY6XQ60VEkh8VHJIBWC8yZA+Tmln0bej2waJHhMpFladq0Kezs7JCYmCg6iuSw+IgE+O03ID+/fNvIywNWrTJIHLJACoUCISEhfIRZCVh8RALcuQMYYrWi+/fLvw2yXEOGDMEvv/yCnJwc0VEkhcVHJEBBgWG2o9UaZjtkmZycnPDWW29h+/btoqNICouPSIAqVQwz8dnZlX8bZNl4uPNZLD4iAVq0KDpHVx4KhQ5t25bzRCFZvN69e+PQoUO4ffu26CiSweIjEqBKFaBPH0BZjr+BSmU+9u3rhuDgYPz6669ceZtKVLFiRfTq1QsbNmwQHUUyWHxEgowbB5RnUXY3twq4dGkjmjdvjk8++QQeHh74/vvvkZqaariQZBG4QO2TWHxEgjRvDnTuDNjYlP69NjbAvHmAo2MNjBkzBqdOncK6detw5coVeHl5oUePHoiKikKBoa6iIbPWsWNH3LlzB6dPnxYdRRL4rE4igXJzAR8f4MwZ4FWvOLe1BWbMAD76qOTvZ2VlYfPmzYiIiMDFixcRHByMsLAwNGjQwHDByexMnDgRer0eU6dOFR1FOBYfkWC5ucCgQcDevUU3tT/vFgXb/z7Sc+VKoH//V9v2hQsXsGLFCqxevRpubm4ICwtD//79UbFiRcOEJ7Nx5swZdOnSBdeuXYNKpRIdRygWH5FEnDoFzJoFbNwIaDRFX1MogMLCotUYxo8HQkKKliUqrYKCAuzevRsRERE4ePAg+vTpg7CwMLRu3RoKQ9xXQWbhrbfewo8//ghfX1/RUYRi8RFJzIMHRSV4/z5gZQW89hrQuLFh7vsDgJs3b2LNmjVYsWIFVCoVQkNDERISAkdHR8N8AEnWnDlz8Mcff8j+QhcWH5FM6fV6JCYmYsWKFdi6dSs6duyIsLAw+Pn5Qa1Wi45HRnDnzh3Uq1cPKSkpsj7czeIjIjx48AA//fQTIiIicP36dQwdOhShoaFwd3cXHY0MrEePHujfvz9CQkJERxGGtzMQESpVqoQPPvgAv/32G/bu3YucnBy0bt0aHTt2RGRkJLKzs0VHJAMpXqdPzjjxEVGJ8vPzsX37dkRERCApKQkDBgxAWFgY3nrrLV4QY8ZycnLg5OSEkydPwtnZWXQcITjxEVGJrKys0LdvX+zevRvJycmoVasW+vXrh6ZNm2Lu3Lm4d++e6IhUBjY2NggMDMS6detERxGGEx8RvTKdTocDBw4gIiIC0dHR6Nq1K8LCwtC5c2coy/PgUTKphIQEfPTRRzh9+rQsp3cWHxGVyf3797F+/XpEREQgPT0dw4YNw7Bhw+Dq6io6Gr2ETqdD3bp1sWXLFjRv3lx0HJPjLhoRlUmVKlXw8ccf49ixY4iKikJaWhqaNWsGPz8//PTTT8gr77pLZDRKpRJBQUGyvZ+PEx8RGUxOTg6ioqIQERGBkydPYvDgwQgLC0Pjxo1FR6OnXLx4Ed7e3khJSYGm+FFBMsGJj4gMxsbGBoMHD8a+ffuQlJQEBwcHdO/eHS1btsTixYuRkZEhOiL9l4eHB9zd3RETEyM6islx4iMio9JqtYiNjUVERARiY2MREBCAsLAwtGvXTpYXVkjJ4sWLceDAAfz000+io5gUi4+ITCYtLQ2RkZGIiIhAXl4eQkND8f7776NWrVqio8lSeno66tSpg6tXr6Jy5cqi45gMD3USkcnUqPH/C+dGRkbi8uXL8PT05MK5glStWhXvvvsutmzZIjqKSXHiIyKhuHCuWNu2bcOMGTNw8OBB0VFMhsVHRJJx/vx5rFixAmvWrOHCuSaSn58PJycnJCUlwc3NTXQck+ChTiKSjPr162Pq1Km4du0avvjiC2zbtg0uLi4YPnw4Dh8+jNLup9++fdtISS2HlZUVBgwYgMjISNFRTIbFR0SSo9FoEBAQgG3btuHs2bPw8PDA0KFD4enpiRkzZuDOnTsv3YZer8eECRNQp04dhIeHl7o05aR4xQa5/B6x+IhI0mrWrIkvvvgCf/75J5YsWYJTp06hXr166NOnD3bt2oXCwsIS31dYWIjp06cjICAAhw4dgkKhkM0/7KXVsmVLKJVK/Pbbb6KjmASLj4jMgkKhgI+PD1atWoVr166hW7dumDJlCmrXro1bt24983qNRgMHBwecP38ew4YNA1D0jEoALMCnKBQKhISEyOYRZry4hYjM2sWLF/HGG2/Aysrqme8lJiZi9OjROHbs2DPfKywsxMqVK7Fnzx60aNECY8eOhbW1tSkiS9LVq1fx1ltvITU11eJ/HzjxEZFZ8/DweKb0ivfnN23aBF9fXwBFT5Aplp2djZkzZ2LHjh344IMPkJiYiO3bt5sutAS98cYbePPNN7Fr1y7RUYyOxUdEFkehUCA/Px8JCQkICgp69LXiQ53btm3DpUuXMHnyZHTt2hVjx47FypUrRUaWhODgYFkc7lSLDkBEZEipqalYsGABNBrNoykGwBPPBd21axc6d+4MT09PAEBsbCw8PDwAFE2GKpXK9MEloG/fvhgzZgzu3r2L6tWri45jNJz4iMii2NvbQ6FQYPXq1YiLi0NiYiLy8vKgUCigUCjw999/o6CgAM2bN4eNjQ0A4MyZM+jatSsAyLb0AKBSpUro3r27xT+0msVHRBalUqVK+O6773DlyhVs3rwZN2/exMaNG7Fp0yYUFBRAq9XCzs7u0QS4Z88eKJXKR9Of3MnhcCcPdRKRxerWrRsAIC8vD2fPnoVGo4Gbm9sTtz9MnToVffv2haurq6iYkuLr64vQ0FCcP38e9evXFx3HKDjxEZHFs7a2RrNmzQAUnevr3r07evToAT8/PzRs2BAff/zxE6/X6XS4evWqLBfOVavVGDx4MNauXSs6itHwPj4ikiWdToezZ8/Cy8sLQNEtEMWHPwsLC/HDDz9gxowZslw4Nzk5GQEBAbh8+TKUSsubj1h8RETPIeeFcxs3boy5c+eiQ4cOoqMYnOVVORGRgZS0cK6Xlxd69uyJrVu3WvTCucUPrrZEnPiIiErh6YVzQ0JCEBoaanEL5964cQOenp5ITU2Fra2t6DgGxYmPiKgU7OzsMHToUCQkJCA+Ph4KhQIdO3ZE27ZtsWLFCmRmZoqOaBC1atXC22+/jW3btomOYnCc+IiIyqmgoADR0dFYsWIFDh48iMDAQISFhaFVq1ZmfUHMunXrEBkZid27d4uOYlAsPiIiA7p58ybWrFmDiIgIqNVqhIWFITg4GI6OjqKjlVpWVhacnZ1x7tw5vP7666LjGAwPdRIRGVDxwrnnz59/YuHcwMBAREdHP7FKhNTZ2dmhV69eWL9+vegoBsWJj4jIyB48eICNGzciIiICqampeP/99xEaGgp3d3fR0V5q//79GDt2LE6cOCE6isFw4iMiMrJKlSphxIgRSEpKwp49e5CTk4PWrVujY8eOiIyMRE5OjuiIz9WhQwekp6fj5MmToqMYDCc+IiIB8vPzsX37dkRERODIkSMYMGAAwsLC0Lx5c8ldEDNp0iQUFBQgPDxcdBSDYPEREQl2/fp1rFq1CitWrICDgwPCwsIwZMgQVK1aVXQ0AMC5c+fQuXNnXLt2DWq1+a9twEOdRESCubi44Ouvv8bff/+NGTNm4PDhw3Bzc8OgQYMQFxf3aOV4URo2bAgnJyfs27dPaA5D4cRHRCRB6enpWL9+PSIiInD//n0MGzYMw4YNE7Z80rx585CUlITIyEghn29ILD4iIok7fvw4IiIisGHDBrRo0QJhYWHo1asXrK2tTZYhLS0NdevWRUpKCuzt7U32ucbA4iMiMhM5OTmIiopCREQETp48iSFDhiAsLAxvvvmmST4/ICAAffr0wdChQ03yecbCc3xERGbCxsYGgwcPxr59+5CUlAR7e3v4+/vj7bffxpIlS4y+cK6lrNjAiY+IyIxptVrExMQgIiICcXFx6NWrF8LCwuDj42Pw2yJyc3Ph5OSE48ePCzvXaAgsPiIiC2GKhXM//PBD1K5dG19++aXBtmlqLD4iIguj1+tx5MgRREREYMuWLWjbti3CwsLQvXt3aDSacm370KFD+OCDD3Bm3z4oTp4EMjKAChUAJyegeXNAYjffl4TFR0RkwQy6cK5eD318PA74+6ODVguljQ2g0xWVnU4HVK0KjB8PhIQAlSoZ/v+MgbD4iIhk4vz581ixYgXWrFkDd3d3hIWFoV+/fqhYseLL35yeDnTrBpw9C11m5vOvjLSzK/p10ybA399Q0Q2KxUdEJDPFC+dGREQgISHh5Qvn3rsHtGgB3LgB5Oe/2ofY2AArVgADBxo2vAGw+IiIZOzxhXM1Gg1CQ0OfXDhXqwVatgTOnHn10itmYwPs2we0bm344OXA4iMiIuj1eiQmJiIiIgJbt25F586dERYWBr/8fKiCg4HMzLJtuE0b4NAhw4YtJxYfERE94fGFc+edOIG3SzvpPa5CBeDUKaBuXcMFLCcWHxERlezvv6Hz9IQyL6/s29BogA8/BObNM1yucuIjy4iIqGT//jeU5bzvDwUFQGysYfIYCIuPiIhK9p//FBVXeRn5GaKlxeIjIqKSaTSA0gA1Ud6p0cBYfEREVLLXXgPUasNsR0JYfEREVDI/v6L7+MqjYkVgxAjD5DEQFh8REZXM1hYYOrR8hyp1OmDwYINFMgQWHxERPd9nnwEqVdnea21d9MDq4ud3SgSLj4iInq9ePWDy5KLprzRUqqKlin780Ti5yoHFR0RELzZxIvDxx69eflZWgIsLcPAg4OBg3GxlwOIjIqIXUyiAadOAhQuBmjWLLlgpiY1N0SPKAgOB48eLJj4J4iPLiIjo1el0QFxcUREeOwZkZRVNeDVqACNHAqGhQLVqolO+EIuPiIhkhYc6iYhIVlh8REQkKyw+IiKSFRYfERHJCouPiIhkhcVHRESywuIjIiJZYfEREZGssPiIiEhWWHxERCQrLD4iIpIVFh8REckKi4+IiGSFxUdERLLC4iMiIllh8RERkayw+IiISFZYfEREJCssPiIikhUWHxERyQqLj4iIZIXFR0REsvJ/vd9cvaWodjwAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "layout = nx.random_layout(G, seed=10)\n",
    "colors = ['r', 'g', 'b', 'y']\n",
    "nx.draw(G, layout, node_color=colors)\n",
    "labels = nx.get_edge_attributes(G, 'weight')\n",
    "nx.draw_networkx_edge_labels(G, pos=layout, edge_labels=labels);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The brute-force method is as follows. Basically, we exhaustively try all the binary assignments. In each binary assignment, the entry of a vertex is either 0 (meaning the vertex is in the first partition) or 1 (meaning the vertex is in the second partition). We print the binary assignment that satisfies the definition of the graph partition and corresponds to the minimal number of crossing edges."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Objective value computed by the brute-force method is 3\n"
     ]
    }
   ],
   "source": [
    "def objective_value(x: np.ndarray, w: np.ndarray) -> float:\n",
    "    \"\"\"Compute the value of a cut.\n",
    "    Args:\n",
    "        x: Binary string as numpy array.\n",
    "        w: Adjacency matrix.\n",
    "    Returns:\n",
    "        Value of the cut.\n",
    "    \"\"\"\n",
    "    X = np.outer(x, (1 - x))\n",
    "    w_01 = np.where(w != 0, 1, 0)\n",
    "    return np.sum(w_01 * X)\n",
    "\n",
    "def bitfield(n: int, L: int) -> list[int]:\n",
    "    result = np.binary_repr(n, L)\n",
    "    return [int(digit) for digit in result]  # [2:] to chop off the \"0b\" part\n",
    "\n",
    "# use the brute-force way to generate the oracle\n",
    "L = num_nodes\n",
    "max = 2**L\n",
    "sol = np.inf\n",
    "for i in range(max):\n",
    "    cur = bitfield(i, L)\n",
    "\n",
    "    how_many_nonzero = np.count_nonzero(cur)\n",
    "    if how_many_nonzero * 2 != L:  # not balanced\n",
    "        continue\n",
    "\n",
    "    cur_v = objective_value(np.array(cur), w)\n",
    "    if cur_v < sol:\n",
    "        sol = cur_v\n",
    "\n",
    "print(f'Objective value computed by the brute-force method is {sol}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The graph partition problem can be converted to an Ising Hamiltonian. Qiskit has different capabilities in the Optimization module to do this. Here, since the goal is to show QAOA, the module is used without further explanation to create the operator. The paper [Ising formulations of many NP problems](https://arxiv.org/abs/1302.5843) may be of interest if you would like to understand the technique further."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit.quantum_info import Pauli, SparsePauliOp\n",
    "\n",
    "def get_operator(weight_matrix: np.ndarray) -> tuple[SparsePauliOp, float]:\n",
    "    r\"\"\"Generate Hamiltonian for the graph partitioning\n",
    "    Notes:\n",
    "        Goals:\n",
    "            1 Separate the vertices into two set of the same size.\n",
    "            2 Make sure the number of edges between the two set is minimized.\n",
    "        Hamiltonian:\n",
    "            H = H_A + H_B\n",
    "            H_A = sum\\_{(i,j)\\in E}{(1-ZiZj)/2}\n",
    "            H_B = (sum_{i}{Zi})^2 = sum_{i}{Zi^2}+sum_{i!=j}{ZiZj}\n",
    "            H_A is for achieving goal 2 and H_B is for achieving goal 1.\n",
    "    Args:\n",
    "        weight_matrix: Adjacency matrix.\n",
    "    Returns:\n",
    "        Operator for the Hamiltonian\n",
    "        A constant shift for the obj function.\n",
    "    \"\"\"\n",
    "    num_nodes = len(weight_matrix)\n",
    "    pauli_list = []\n",
    "    coeffs = []\n",
    "    shift = 0\n",
    "\n",
    "    for i in range(num_nodes):\n",
    "        for j in range(i):\n",
    "            if weight_matrix[i, j] != 0:\n",
    "                x_p = np.zeros(num_nodes, dtype=bool)\n",
    "                z_p = np.zeros(num_nodes, dtype=bool)\n",
    "                z_p[i] = True\n",
    "                z_p[j] = True\n",
    "                pauli_list.append(Pauli((z_p, x_p)))\n",
    "                coeffs.append(-0.5)\n",
    "                shift += 0.5\n",
    "\n",
    "    for i in range(num_nodes):\n",
    "        for j in range(num_nodes):\n",
    "            if i != j:\n",
    "                x_p = np.zeros(num_nodes, dtype=bool)\n",
    "                z_p = np.zeros(num_nodes, dtype=bool)\n",
    "                z_p[i] = True\n",
    "                z_p[j] = True\n",
    "                pauli_list.append(Pauli((z_p, x_p)))\n",
    "                coeffs.append(1.0)\n",
    "            else:\n",
    "                shift += 1\n",
    "                \n",
    "    return SparsePauliOp(pauli_list, coeffs=coeffs), shift\n",
    "\n",
    "qubit_op, offset = get_operator(w)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So lets use the QAOA algorithm to find the solution."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1 1 0 0]\n",
      "Objective value computed by QAOA is 3\n"
     ]
    }
   ],
   "source": [
    "from qiskit.algorithms.minimum_eigensolvers import QAOA\n",
    "from qiskit.algorithms.optimizers import COBYLA\n",
    "from qiskit.circuit.library import TwoLocal\n",
    "from qiskit.primitives import Sampler\n",
    "from qiskit.quantum_info import Pauli, Statevector\n",
    "from qiskit.result import QuasiDistribution\n",
    "from qiskit.utils import algorithm_globals\n",
    "\n",
    "sampler = Sampler()\n",
    "\n",
    "\n",
    "def sample_most_likely(state_vector: QuasiDistribution | Statevector) -> np.ndarray:\n",
    "    \"\"\"Compute the most likely binary string from state vector.\n",
    "    Args:\n",
    "        state_vector: State vector or quasi-distribution.\n",
    "\n",
    "    Returns:\n",
    "        Binary string as an array of ints.\n",
    "    \"\"\"\n",
    "    if isinstance(state_vector, QuasiDistribution):\n",
    "        values = list(state_vector.values())\n",
    "    else:\n",
    "        values = state_vector\n",
    "    n = int(np.log2(len(values)))\n",
    "    k = np.argmax(np.abs(values))\n",
    "    x = bitfield(k, n)\n",
    "    x.reverse()\n",
    "    return np.asarray(x)\n",
    "\n",
    "algorithm_globals.random_seed = 10598\n",
    "\n",
    "optimizer = COBYLA()\n",
    "qaoa = QAOA(sampler, optimizer, reps=2)\n",
    "\n",
    "result = qaoa.compute_minimum_eigenvalue(qubit_op)\n",
    "\n",
    "x = sample_most_likely(result.eigenstate)\n",
    "\n",
    "print(x)\n",
    "print(f'Objective value computed by QAOA is {objective_value(x, w)}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The outcome can be seen to match to the value computed above by brute force. But we can also use the classical `NumPyMinimumEigensolver` to do the computation, which may be useful as a reference without doing things by brute force."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1 1 0 0]\n",
      "Objective value computed by the NumPyMinimumEigensolver is 3\n"
     ]
    }
   ],
   "source": [
    "from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver\n",
    "from qiskit.quantum_info import Operator\n",
    "\n",
    "npme = NumPyMinimumEigensolver()\n",
    "result = npme.compute_minimum_eigenvalue(Operator(qubit_op))\n",
    "\n",
    "x = sample_most_likely(result.eigenstate)\n",
    "\n",
    "print(x)\n",
    "print(f'Objective value computed by the NumPyMinimumEigensolver is {objective_value(x, w)}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It is also possible to use VQE as is shown below"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0 1 0 1]\n",
      "Objective value computed by VQE is 3\n"
     ]
    }
   ],
   "source": [
    "from qiskit.algorithms.minimum_eigensolvers import SamplingVQE\n",
    "from qiskit.circuit.library import TwoLocal\n",
    "from qiskit.utils import algorithm_globals\n",
    "\n",
    "algorithm_globals.random_seed = 10598\n",
    "\n",
    "optimizer = COBYLA()\n",
    "ansatz = TwoLocal(qubit_op.num_qubits, \"ry\", \"cz\", reps=2, entanglement=\"linear\")\n",
    "sampling_vqe = SamplingVQE(sampler, ansatz, optimizer)\n",
    "\n",
    "result = sampling_vqe.compute_minimum_eigenvalue(qubit_op)\n",
    "\n",
    "x = sample_most_likely(result.eigenstate)\n",
    "\n",
    "print(x)\n",
    "print(f\"Objective value computed by VQE is {objective_value(x, w)}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<h3>Version Information</h3><table><tr><th>Qiskit Software</th><th>Version</th></tr><tr><td><code>qiskit-terra</code></td><td>0.23.0</td></tr><tr><td><code>qiskit-aer</code></td><td>0.11.1</td></tr><tr><td><code>qiskit-nature</code></td><td>0.6.0</td></tr><tr><td><code>qiskit-finance</code></td><td>0.4.0</td></tr><tr><td><code>qiskit-optimization</code></td><td>0.5.0</td></tr><tr><td><code>qiskit-machine-learning</code></td><td>0.6.0</td></tr><tr><th>System information</th></tr><tr><td>Python version</td><td>3.9.13</td></tr><tr><td>Python compiler</td><td>Clang 13.0.1 </td></tr><tr><td>Python build</td><td>main, May 27 2022 17:01:00</td></tr><tr><td>OS</td><td>Darwin</td></tr><tr><td>CPUs</td><td>2</td></tr><tr><td>Memory (Gb)</td><td>12.0</td></tr><tr><td colspan='2'>Sun Jan 08 11:35:33 2023 EST</td></tr></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div style='width: 100%; background-color:#d5d9e0;padding-left: 10px; padding-bottom: 10px; padding-right: 10px; padding-top: 5px'><h3>This code is a part of Qiskit</h3><p>&copy; Copyright IBM 2017, 2023.</p><p>This code is licensed under the Apache License, Version 2.0. You may<br>obtain a copy of this license in the LICENSE.txt file in the root directory<br> of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.<p>Any modifications or derivative works of this code must retain this<br>copyright notice, and modified files need to carry a notice indicating<br>that they have been altered from the originals.</p></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import qiskit.tools.jupyter\n",
    "%qiskit_version_table\n",
    "%qiskit_copyright"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.13"
  },
  "vscode": {
   "interpreter": {
    "hash": "0becea4cb9c4294abbba7f0b15d5de98241be600556705f5379b48b9de7cb1f9"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
